<?php

namespace App\Http\Clients;

use App\Enum\CodeEnum;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Session;

class ZccmzjgyxmClient
{
    public $appId;

    public $secert;
    private $_tokenKey = 'ZccmzjgyxmClient-token';
    private $_tokenExpireKey = 'ZccmzjgyxmClient-token-expire';
    private $_redis = null;
    private $_redisTokenKey = 'ZCCMZJGYXM-token-key';
    /**
     * 村民之家 - 正确 code
     */
    public const CMZJ_CODE_SUCCESS=200;
    /**
     * 村民之家 - 错误 code
     */
    public const CMZJ_CODE_ERROR=400;

    public function __construct()
    {
        $this->appId = env('ZCCMZJGYXM_APP_ID');
        $this->secert = env('ZCCMZJGYXM_SECERT');
        $this->_redis = app("redis.connection");
    }
    private function _saveToken($token, $expireTime)
    {
        $this->_redis->set($this->_redisTokenKey, $token, "EX", $expireTime);
    }

    private function _getToken()
    {
        if( $this->_redis->exists($this->_redisTokenKey)){
            return $this->_redis->get($this->_redisTokenKey);
        }
        return $this->_accesstokenHttpClient();
    }

    // TODO 需要根据业务更换
    private static function getHostURL(): string
    {
        return config('resource.zccmzjgyxm.http_uri');
    }

    // TODO 需要根据业务更换
    private static function getTimeout(): int
    {
        return 3000;
    }

    private static function getHeaders(): array
    {
        return [
            'Content-Type' => 'application/json;charset=UTF-8',
            'Accept' => 'application/json'
        ];
    }

    public static function httpClient(): Client
    {
        return new Client([
            'base_uri' => self::getHostURL(),
            'timeout' => self::getTimeout(),
            'headers' => self::getHeaders()
        ]);
    }

    /**
     * 生成密钥
     * @return bool
     * @throws GuzzleException
     */
    private function _accesstokenHttpClient()
    {
        $method = config('resource.zccmzjgyxm.accesstoken');
        $url = $method."?appid=".$this->appId."&secert=".$this->secert;
        $this->_logInfo( $method, $url );

        $data = $this->_callHttpClient($method, $url);
        if (empty($data) ){

            $this->_logError( $method, 'ERROR, failed fetch token.' );
            return false;
        }
        //
        $data = @json_decode($data, true);
        if ( isset($data['code']) && $data['code'] == CodeEnum::ERROR ){
            $this->_logError( $method, 'ERROR, failed fetch token.'.$data['msg'] );
            return false;
        }

        // 保存token到...
        $this->_saveToken($data['data']['token'], $data['data']['expires_in']);


        return $data['data']['token'];
    }

    /**
     * 获取某个用户信息
     * $phone
     * $token
     * @throws GuzzleException
     */
    private function _getUserInfoHttpClient($phone)
    {
        $token = $this->_getToken();
        $method = config('resource.zccmzjgyxm.get_user_info');
        $url = $method."?appid=".$this->appId."&phone=".$phone."&token=".$token;

        $result  =  $this->_callHttpClient($method, $url);
        return json_decode($result, true);
    }

    /**
     * 获取某个用户的捐赠积分
     * @param $phone
     * @return mixed|string
     * @throws GuzzleException
     */
    private function _getUserScoreHttpClient($phone)
    {
        $token = $this->_getToken();

        $method = config('resource.zccmzjgyxm.get_user_score');
        $url = $method."?appid=".$this->appId."&phone=".$phone."&token=".$token;

        $result  =  $this->_callHttpClient($method, $url);
        return json_decode($result, true);
    }

    /**
     * 消耗某个用户的捐赠积分
     * @param $phone
     * @param $score
     * @param $orderNo
     * @param $products
     * @return mixed|string
     * @throws GuzzleException
     */
    private function _consumeUserScoreHttpClient($phone, $score, $orderNo, $products)
    {
        $token = $this->_getToken();

        $method = config('resource.zccmzjgyxm.consume_user_score');
        $url = $method."?appid=".$this->appId."&phone=".$phone."&token=".$token."&score=".$score."&order_no=".$orderNo."&products=".$products;

        $result  =  $this->_callHttpClient($method, $url);
        return json_decode($result, true);

    }

    /**
     * 返还某个用户的捐赠积分
     * @param $phone
     * @param $score
     * @param $orderNo
     * @return mixed|string
     * @throws GuzzleException
     */
    private function _returnUserScoreHttpClient($phone, $score, $orderNo)
    {
        $token = $this->_getToken();

        $method = config('resource.zccmzjgyxm.return_user_score');
        $url = $method."?appid=".$this->appId."&phone=".$phone."&token=".$token."&score=".$score."&order_no=".$orderNo;

        $result  =  $this->_callHttpClient($method, $url);
        return json_decode($result, true);
    }
    /**
     * 预付费冻结完成
     * @param $advanceNo
     * @param $score
     * @return mixed|string
     * @throws GuzzleException
     */
    private function _advanceScoreSuccess($advanceNo, $score)
    {
        $token = $this->_getToken();

        $method = config('resource.zccmzjgyxm.advance_score_success');
        $url = $method."?appid=".$this->appId."&advanceNo=".$advanceNo."&token=".$token."&score=".$score;

        $result  =  $this->_callHttpClient($method, $url);
        return json_decode($result, true);
    }

    /**
     * 预付费冻结申请
     * @param $phone
     * @param $score
     * @return mixed|string
     * @throws GuzzleException
     */
    private function _advanceScoreApply($phone,$score)
    {
        $token = $this->_getToken();

        $method = config('resource.zccmzjgyxm.advance_score_apply');
        $url = $method."?appid=".$this->appId."&phone=".$phone."&token=".$token."&score=".$score;

        $result  =  $this->_callHttpClient($method, $url);
        $result  = json_decode($result, true);

        return $result;
    }

    /**
     * 预付费冻结取消
     * @param $advanceNo
     * @return mixed|string
     * @throws GuzzleException
     */
    private function _advanceScoreCancel($advanceNo)
    {
        $token = $this->_getToken();

        $method = config('resource.zccmzjgyxm.advance_score_cancel');
        $url = $method."?appid=".$this->appId."&advanceNo=".$advanceNo."&token=".$token;

        $result  =  $this->_callHttpClient($method, $url);
        return json_decode($result, true);
    }

    /**
     * 获取省市区县
     * 返回完整的4级树形结构。20231018 by LC
     * @return mixed
     * @throws GuzzleException
     */
    private function _getCityTree()
    {
        $token = $this->_getToken();

        $method = config('resource.zccmzjgyxm.get_city_tree');
        $url = $method."?appid=".$this->appId."&token=".$token;

        $result  =  $this->_callHttpClient($method, $url);
        return json_decode($result, true);
    }

    /**
     * 获取下级区域。20231018 by LC
     * 根据上级Code返回下级区域信息。
     * @param $code
     * @return mixed
     * @throws GuzzleException
     */
    private function _getCityMaps($code=null)
    {
        $token = $this->_getToken();

        $method = config('resource.zccmzjgyxm.get_city_maps');
        $url = $method."?appid=".$this->appId."&token=".$token.'&code='.$code;

        $result  =  $this->_callHttpClient($method, $url);
        return json_decode($result, true);
    }

    /**
     * 获取村委/居委
     * 根据街道/镇/乡Code返回相应的村委/居委信息。
     * @param $code string  街道/镇/乡Code *
     * @return mixed
     * @throws GuzzleException
     */
    private function _getCommitteeByVillage($code)
    {
        $token = $this->_getToken();

        $method = config('resource.zccmzjgyxm.get_committee_by_village');
        $url = $method."?appid=".$this->appId."&token=".$token.'&code='.$code;

        $result  =  $this->_callHttpClient($method, $url);
        return json_decode($result, true);
    }

    /**
     * 打印日志 20230803 by LC
     *
     * @param string $method
     * @param string $msg
     * @return void
     */
    private function _logInfo(string $method, string $msg)
    {
        Log::info( 'METHOD=: ['.$method .'], msg= ' . $msg );
    }
    private function _logError($method, $msg)
    {
        Log::error( '<<<<<<'.$method .'>>>>>>>, MSG: '.$msg );
    }

    /**
     * 調用三方方法
     * @param $method
     * @param $url
     * @return string
     * @throws GuzzleException
     */
    private function _callHttpClient( $method, $url )
    {
        try {
            $response = self::httpClient()->request('GET', $url);
            $result = $response->getBody()->getContents();
            Log::notice(sprintf("[METHOD]=%s, [RESULT]=%s", $method, var_export($result,true)));
            return $result;
        } catch (\Exception $ex) {
            $this->_logError($method, $ex->getMessage());
            return false;
        }
    }

    /**
     * 消耗用户积分
     *
     * @param $phone
     * @param $orderNo
     * @param $score
     * @param $products
     * @return mixed|string
     * @throws GuzzleException
     */
    public function consumeCredits($phone, $orderNo, $score, $products)
    {
        return $this->_consumeUserScoreHttpClient($phone, $score, $orderNo, $products);
    }

    /**
     * 获取用户积分
     * @param $phone
     * @return mixed|string
     * @throws GuzzleException
     */
    public function getUserConsume($phone)
    {
        return $this->_getUserScoreHttpClient($phone);
    }

    /**
     * 返还用户积分
     * @param $phone
     * @param $orderNo
     * @param $score
     * @return mixed|string
     * @throws GuzzleException
     */
    public function returnCredits($phone, $orderNo, $score)
    {
        return $this->_returnUserScoreHttpClient($phone, $score, $orderNo);
    }

    /**
     * 获取用户基本信息
     * @param $phone
     * @return mixed|string
     * @throws GuzzleException
     */
    public function getUserInfo($phone)
    {
        return $this->_getUserInfoHttpClient($phone);
    }
    /**
     * 该接⼝将冻结某个⽤⼾的指定积分并影响剩余积分，⽤于在后续完成事件中进⾏扣除。
     * @param $phone
     * @param $score
     * @return mixed|string
     * @throws GuzzleException
     */
    public function advanceScoreApply($phone,$score)
    {
        return $this->_advanceScoreApply($phone,$score);
    }
    /**
     * 该接⼝将⽤于将冻结的积分进⾏实际扣除。
     * @param $advanceNo
     * @param $score
     * @return mixed|string
     * @throws GuzzleException
     */
    public function advanceScoreSuccess($advanceNo, $score)
    {
        return $this->_advanceScoreSuccess($advanceNo, $score);
    }
    /**
     * 该接⼝将预付申请进⾏取消，返还⽤⼾剩余积分。
     * @param $advanceNo
     * @return mixed|string
     * @throws GuzzleException
     */
    public function advanceScoreCancel($advanceNo)
    {
        return $this->_advanceScoreCancel($advanceNo);
    }

    /**
     * 获取省市区县
     * 返回完整的4级树形结构。
     * @return mixed
     * @throws GuzzleException
     */
    public function getCityTree()
    {
        return $this->_getCityTree();
    }

    /**
     * 获取下级区域
     * 根据上级Code返回下级区域信息。
     * @param $code
     * @return mixed
     * @throws GuzzleException
     */
    public function getCityMaps($code=null)
    {
        return $this->_getCityMaps($code);
    }

    /**
     * 获取村委/居委
     * 根据街道/镇/乡Code返回相应的村委/居委信息。
     * @param $code
     * @return mixed
     * @throws GuzzleException
     */
    public function getCommitteeByVillage($code)
    {
        return $this->_getCommitteeByVillage($code);
    }
}
